#pragma once
#include <iostream>
#include <string>
#include <functional>

#include <sys/select.h>
#include <poll.h>
#include "sock.hpp"
#include "err.hpp"

namespace YZC
{
    static const uint16_t defaultport = 8080;
    static const int fdnum = 2028;
    static const int defaultfd = -1;
    using func_t = std::function<std::string(const std::string &)>;

    class pollServer
    {
    public:
        pollServer(func_t func, int port = defaultport)
            : _port(port), _listensock(-1), _rfds(nullptr), _callback(func) {}
        ~pollServer()
        {
            if (_listensock > 0)
                close(_listensock);
            _listensock = -1;

            if (_rfds)
                delete[] _rfds;
            _rfds = nullptr;
        }

        void Print()
        {
            std::cout << "fd list:";
            for (int i = 0; i < fdnum; i++)
            {
                if (_rfds[i].fd != defaultfd)
                    std::cout << _rfds[i].fd << " ";
            }
            std::cout << std::endl;
        }

        void init()
        {
            // 创建套接字
            _listensock = Sock::creatSock();
            // 绑定端口
            Sock::Bind(_listensock, _port);
            // 监听
            Sock::Listen(_listensock);

            // 初始化保存文件描述符集合,fd_set是内核类型，它一定是有大小的！即我们的fdarray是有上限的！
            _rfds = new pollfd[fdnum]; // fd_set 128字节！可以表示128*8 = 1024 个套接字！
            for (int i = 0; i < fdnum; i++)
                ResetItem(i);

            // 提前监听listen套接字，如果之后有新连接到来时候。poll告诉用户需要调用accept获取新连接
            _rfds[0].fd = _listensock;
            _rfds[0].events = POLLIN;
        }

        void run()
        {
            int timeout = 1000;
            for (;;)
            {
                int n = poll(_rfds, fdnum, timeout);
                switch (n)
                {
                case 0:
                    LogMessage(NORMAL, "timeout...");
                    break;
                case -1:
                    LogMessage(ERROR, "poll error errno:%d errstr:%s", errno, strerror(errno));
                    exit(POLL_ERR);
                default:
                    // 监听事件就绪了！即表示有新连接来了！
                    LogMessage(NORMAL, "poll tell me a event ready!, I can accept!");
                    HandlerReadEvent();
                    break;
                }
            }
        }

    private:
        void ResetItem(int i)
        {
            _rfds[i].fd = defaultfd;
            _rfds[i].events = 0;
            _rfds[i].revents = 0;
        }
        // 注意，此时HandlerEvent可能有多个fd就绪了，不止是一个fd就绪
        // 目前我们只处理了read事件
        void HandlerReadEvent()
        {
            LogMessage(DEBUG, "HandlerEvent in");

            // 遍历所有文件描述符
            for (int i = 0; i < fdnum; i++)
            {
                // 过滤非法的fd
                if (_rfds[i].fd == defaultfd)
                    continue;
                // 过滤没有设置event的套接字
                if (!(_rfds[i].events & POLLIN))
                    continue;

                if (_rfds[i].fd == _listensock && (_rfds[i].revents & POLLIN))
                {
                    // 获取新连接就绪
                    Accepter();
                }
                else if ((_rfds[i].revents & POLLIN)) // 注意，这里需要判断这个是否就绪
                {
                    // 处理其他读事件套接字,读事件就绪
                    Recver(i);
                }
                else
                {
                    // 该文件描述符未就绪！
                }
            }
            LogMessage(DEBUG, "HandlerEvent out");
        }

        void Accepter()
        {
            LogMessage(DEBUG, "Accepter in");

            // select告诉我,listensock套接字读事件就绪了（监听连接就绪了）需要accept拿走数据
            std::string clientip;
            uint16_t clientport = 0;

            int sock = Sock::Accept(_listensock, &clientip, &clientport); // accept 需要等连接 + 接收

            if (sock < 0)
                return;
            LogMessage(NORMAL, "accpet access client ip port[%s:%d]", clientip.c_str(), clientport);

            int i = 0;
            for (; i < fdnum; i++)
            {
                if (_rfds[i].fd != defaultfd)
                    continue;
                else
                    break; // 说明有空的fd供套接字使用
            }

            if (i == fdnum)
            {
                LogMessage(WARNING, "server is full,please wait next!");
                close(sock); // 关闭新套接字，请下一次来
            }
            else
            {
                // 找到了一个空位置！
                _rfds[i].fd = sock;
                _rfds[i].events = POLLIN;
                _rfds[i].revents = 0;
            }
            Print();
            LogMessage(NORMAL, "Accept out!");
        }

        // 用于接收sockfd的数据，index是该fd在_fdarray中的下标
        void Recver(int index)
        {
            LogMessage(DEBUG, "Recver in");

            // 1.读取request
            //  这样读取是有问题的！如何保证你读取的数据是一个完整的报文？
            char buffer[1204];
            // 这里进行读取数据的时候会不会阻塞？  ->  因为读事件就绪才能执行recv,所有不会阻塞
            ssize_t s = recv(_rfds[index].fd, buffer, sizeof(buffer) - 1, 0);
            if (s > 0)
            {
                buffer[s] = 0;
                std::cout << "client#" << buffer << std::endl;
            }
            else if (s == 0) // 客户端退出最后会发送EOF表示文件结束
            {
                close(_rfds[index].fd);
                ResetItem(index);
                LogMessage(NORMAL, "client quit");
                return;
            }
            else
            {
                close(_rfds[index].fd);
                ResetItem(index);
                LogMessage(ERROR, "recv error");
                return;
            }

            // 2.处理request,形成response

            // write bug
            std::string response = _callback(buffer);

            // 3.返回response
            send(_rfds[index].fd, response.c_str(), response.size(), 0);
            LogMessage(DEBUG, "Recver out");
        }

    private:
        int _listensock;
        uint16_t _port;
        struct pollfd *_rfds;
        func_t _callback;
    };
}